'use strict';
// help-function
function addEvent( obj, evt, fnc ) {
    // W3C model
    if ( obj.addEventListener ) {
        obj.addEventListener( evt, fnc, false );
        return true;
    }
    // Microsoft model
    else if ( obj.attachEvent ) {
        return obj.attachEvent( 'on' + evt, fnc );
    }
    // Browser don't support W3C or MSFT model, go on with traditional
    else {
        evt = 'on' + evt;
        if ( typeof obj[ evt ] === 'function' ) {
            // Object already has a function on traditional Let's wrap it with our own function inside another function
            fnc = (function ( f1, f2 ) {
                return function () {
                    f1.apply( this, arguments );
                    f2.apply( this, arguments );
                };
            })( obj[ evt ], fnc );
        }
        obj[ evt ] = fnc;
        return true;
    }
    return false;
}
function removeEvent(elem,eventType,handler) {
    if (elem.removeEventListener)
        elem.removeEventListener (eventType,handler,false);
    if (elem.detachEvent)
        elem.detachEvent ('on'+eventType,handler);
}
function isNumeric( n ) {
    return ! isNaN( parseFloat( n ) ) && isFinite( n );
}
function getRandomInt( min, max ) {
    return Math.floor( Math.random() * ( max - min ) ) + min;
}
function getRandomArbitrary( min, max ) {
    return Math.random() * ( max - min ) + min;
}
// requestAnimationFrame polyfil
( function () {
    var lastTime = 0;
    var vendors  = [ 'ms', 'moz', 'webkit', 'o' ];
    for ( var x = 0; x < vendors.length
    && ! window.requestAnimationFrame; ++ x ) {
        window.requestAnimationFrame = window[ vendors[ x ] + 'RequestAnimationFrame' ];
        window.cancelAnimationFrame  = window[ vendors[ x ] + 'CancelAnimationFrame' ]
            || window[ vendors[ x ] + 'CancelRequestAnimationFrame' ];
    }
    if ( ! window.requestAnimationFrame ) {
        window.requestAnimationFrame = function ( callback, element ) {
            var currTime   = new Date().getTime();
            var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
            var id         = window.setTimeout(
                function () {
                    callback( currTime + timeToCall );
                },
                timeToCall
            );
            lastTime       = currTime + timeToCall;
            return id;
        };
    }
    if ( ! window.cancelAnimationFrame ) {
        window.cancelAnimationFrame = function ( id ) {
            clearTimeout( id );
        };
    }
}() );

// prefomance.now() polyfill
(function(){

    if ("performance" in window == false) {
        window.performance = {};
    }

    Date.now = (Date.now || function () {  // thanks IE8
        return new Date().getTime();
    });

    if ("now" in window.performance == false){

        var nowOffset = Date.now();

        if (performance.timing && performance.timing.navigationStart){
            nowOffset = performance.timing.navigationStart
        }

        window.performance.now = function now(){
            return Date.now() - nowOffset;
        }
    }

})();

if ( ! document.getElementsByClassName ) {
    document.getElementsByClassName = function ( search ) {
        var d = document, elements, pattern, i, results = [];
        if ( d.querySelectorAll ) { // IE8
            return d.querySelectorAll( "." + search );
        }
        if ( d.evaluate ) { // IE6, IE7
            pattern  = ".///[contains(concat(' ', @class, ' '), ' " + search + " ')]";
            elements = d.evaluate( pattern, d, null, 0, null );
            while ( ( i = elements.iterateNext() ) ) {
                results.push( i );
            }
        }
        else {
            elements = d.getElementsByTagName( "*" );
            pattern  = new RegExp( "(^|\\s)" + search + "(\\s|$)" );
            for ( i = 0; i < elements.length; i ++ ) {
                if ( pattern.test( elements[ i ].className ) ) {
                    results.push( elements[ i ] );
                }
            }
        }
        return results;
    };
}
//CanvasRenderingContext2D.renderText extension
if ( CanvasRenderingContext2D && ! CanvasRenderingContext2D.renderText ) {
    // @param  letterSpacing  {float}  CSS letter-spacing property
    CanvasRenderingContext2D.prototype.renderText = function ( text, x, y, letterSpacing ) {
        if ( ! text || typeof text !== 'string' || text.length === 0 ) {
            return;
        }

        if ( typeof letterSpacing === 'undefined' ) {
            letterSpacing = 0;
        }

        // letterSpacing of 0 means normal letter-spacing

        var characters = String.prototype.split.call( text, '' ), index = 0, current, currentPosition = x, align = 1;
        if ( this.textAlign === 'right' ) {
            characters = characters.reverse();
            align      = - 1;
        }
        else if ( this.textAlign === 'center' ) {
            var totalWidth = 0;
            for ( var i = 0; i < characters.length; i ++ ) {
                totalWidth += ( this.measureText( characters[ i ] ).width + letterSpacing );
            }
            currentPosition = x - ( totalWidth / 2 );
        }

        while ( index < text.length ) {
            current = characters[ index ++ ];
            this.fillText( current, currentPosition, y );
            currentPosition += ( align * ( this.measureText( current ).width + letterSpacing ) );
        }
    };
}
// classList polyfill
/*
 * classList.js: Cross-browser full element.classList implementation.
 * 1.1.20150312
 *
 * By Eli Grey, http://eligrey.com
 * License: Dedicated to the public domain.
 *   See https://github.com/eligrey/classList.js/blob/master/LICENSE.md
 */

/*global self, document, DOMException */

/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */

if ("document" in self) {

// Full polyfill for browsers with no classList support
// Including IE < Edge missing SVGElement.classList
if (!("classList" in document.createElement("_")) 
    || document.createElementNS && !("classList" in document.createElementNS("http://www.w3.org/2000/svg","g"))) {

(function (view) {

"use strict";

if (!('Element' in view)) return;

var
      classListProp = "classList"
    , protoProp = "prototype"
    , elemCtrProto = view.Element[protoProp]
    , objCtr = Object
    , strTrim = String[protoProp].trim || function () {
        return this.replace(/^\s+|\s+$/g, "");
    }
    , arrIndexOf = Array[protoProp].indexOf || function (item) {
        var
              i = 0
            , len = this.length
        ;
        for (; i < len; i++) {
            if (i in this && this[i] === item) {
                return i;
            }
        }
        return -1;
    }
    // Vendors: please allow content code to instantiate DOMExceptions
    , DOMEx = function (type, message) {
        this.name = type;
        this.code = DOMException[type];
        this.message = message;
    }
    , checkTokenAndGetIndex = function (classList, token) {
        if (token === "") {
            throw new DOMEx(
                  "SYNTAX_ERR"
                , "An invalid or illegal string was specified"
            );
        }
        if (/\s/.test(token)) {
            throw new DOMEx(
                  "INVALID_CHARACTER_ERR"
                , "String contains an invalid character"
            );
        }
        return arrIndexOf.call(classList, token);
    }
    , ClassList = function (elem) {
        var
              trimmedClasses = strTrim.call(elem.getAttribute("class") || "")
            , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
            , i = 0
            , len = classes.length
        ;
        for (; i < len; i++) {
            this.push(classes[i]);
        }
        this._updateClassName = function () {
            elem.setAttribute("class", this.toString());
        };
    }
    , classListProto = ClassList[protoProp] = []
    , classListGetter = function () {
        return new ClassList(this);
    }
;
// Most DOMException implementations don't allow calling DOMException's toString()
// on non-DOMExceptions. Error's toString() is sufficient here.
DOMEx[protoProp] = Error[protoProp];
classListProto.item = function (i) {
    return this[i] || null;
};
classListProto.contains = function (token) {
    token += "";
    return checkTokenAndGetIndex(this, token) !== -1;
};
classListProto.add = function () {
    var
          tokens = arguments
        , i = 0
        , l = tokens.length
        , token
        , updated = false
    ;
    do {
        token = tokens[i] + "";
        if (checkTokenAndGetIndex(this, token) === -1) {
            this.push(token);
            updated = true;
        }
    }
    while (++i < l);

    if (updated) {
        this._updateClassName();
    }
};
classListProto.remove = function () {
    var
          tokens = arguments
        , i = 0
        , l = tokens.length
        , token
        , updated = false
        , index
    ;
    do {
        token = tokens[i] + "";
        index = checkTokenAndGetIndex(this, token);
        while (index !== -1) {
            this.splice(index, 1);
            updated = true;
            index = checkTokenAndGetIndex(this, token);
        }
    }
    while (++i < l);

    if (updated) {
        this._updateClassName();
    }
};
classListProto.toggle = function (token, force) {
    token += "";

    var
          result = this.contains(token)
        , method = result ?
            force !== true && "remove"
        :
            force !== false && "add"
    ;

    if (method) {
        this[method](token);
    }

    if (force === true || force === false) {
        return force;
    } else {
        return !result;
    }
};
classListProto.toString = function () {
    return this.join(" ");
};

if (objCtr.defineProperty) {
    var classListPropDesc = {
          get: classListGetter
        , enumerable: true
        , configurable: true
    };
    try {
        objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
    } catch (ex) { // IE 8 doesn't support enumerable:true
        if (ex.number === -0x7FF5EC54) {
            classListPropDesc.enumerable = false;
            objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
        }
    }
} else if (objCtr[protoProp].__defineGetter__) {
    elemCtrProto.__defineGetter__(classListProp, classListGetter);
}

}(self));

} else {
// There is full or partial native classList support, so just check if we need
// to normalize the add/remove and toggle APIs.

(function () {
    "use strict";

    var testElement = document.createElement("_");

    testElement.classList.add("c1", "c2");

    // Polyfill for IE 10/11 and Firefox <26, where classList.add and
    // classList.remove exist but support only one argument at a time.
    if (!testElement.classList.contains("c2")) {
        var createMethod = function(method) {
            var original = DOMTokenList.prototype[method];

            DOMTokenList.prototype[method] = function(token) {
                var i, len = arguments.length;

                for (i = 0; i < len; i++) {
                    token = arguments[i];
                    original.call(this, token);
                }
            };
        };
        createMethod('add');
        createMethod('remove');
    }

    testElement.classList.toggle("c3", false);

    // Polyfill for IE 10 and Firefox <24, where classList.toggle does not
    // support the second argument.
    if (testElement.classList.contains("c3")) {
        var _toggle = DOMTokenList.prototype.toggle;

        DOMTokenList.prototype.toggle = function(token, force) {
            if (1 in arguments && !this.contains(token) === !force) {
                return force;
            } else {
                return _toggle.call(this, token);
            }
        };

    }

    testElement = null;
}());

}

}